﻿/* HEADER
 * ------
 * © 2009 by Salomon Zwecker 
 * modified by:
 * - 
 */
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Sampler
{

    /// <summary>
    /// A class to manage Samples and switch between them
    /// </summary>
    public sealed class SampleManager
    {
        /// <summary>
        /// The Instance of the SampleManager. use it to get access to the member variables.
        /// </summary>
        public static SampleManager Instance { get; private set; }

        /// <summary>
        /// The text color of the description text of the samples
        /// </summary>
        public Color SampleTextColor = Color.White;
        /// <summary>
        /// The text color of the manager introduction text
        /// </summary>
        public Color ManagerTextColor = Color.White;
        
        KeyboardState _PreviousKeys;
        GamePadState _PreviousButtons;
        MouseState _PreviousMouse;

#if !ZUNE

        Keys _HelpKey = Keys.F1;
        Buttons _HelpButton = Buttons.Back;

        /// <summary>
        /// The Key which is used to switch to the next sample
        /// </summary>
        public Keys NextSampleKey = Keys.PageDown;
        /// <summary>
        /// The Key which is used to switch to the previous sample
        /// </summary>
        public Keys PreviousSampleKey = Keys.PageUp;
        /// <summary>
        /// The Button which is used to switch to the next sample
        /// </summary>
        public Buttons NextSampleButton = Buttons.RightShoulder;
        /// <summary>
        /// The Button which is used to switch to the previous sample
        /// </summary>
        public Buttons PreviousSampleButton = Buttons.LeftShoulder;
#else
        /// <summary>
        /// The Button which is used to switch to the next sample
        /// </summary>
        public Buttons NextSampleButton = Buttons.Back;
#endif
        /// <summary>
        /// Gets the Font which is used to display the descriptions
        /// </summary>
        public SpriteFont Font { get; private set; }

        List<Sample> _Samples = new List<Sample>();
        internal Sample _ActiveSample;

        string _Description = "";
        Vector2 _DescriptionPosition, _FpsPosition;
        /// <summary>
        /// a Reference to the Game class
        /// </summary>
        public Game Game { get; private set; }

        GraphicsDeviceManager _GraphicsDeviceManager;

        bool _IsDescriptionVisible = true;

        float _FramesPerSecond;

        /// <summary>
        /// Creates a new (and the only) Instance of the SampleManager.
        /// The SampleManager must be created before a Sample is created.
        /// </summary>
        /// <param name="game">the Game class</param>
        /// <param name="graphics">the GraphicsDeviceManager which normally is created within the game class</param>
        /// <param name="font">the font for all the text (for samples and introduction)</param>
        public SampleManager(Game game, GraphicsDeviceManager graphics, SpriteFont font)
        {
            if (Instance == null)
                Instance = this;
            else
                throw new Exception("Only one SampleManager allowed.");

            Game = game;
            _GraphicsDeviceManager = graphics;
            Font = font;

            _PreviousButtons = GamePad.GetState(PlayerIndex.One);
            _PreviousKeys = Keyboard.GetState();
        }

        /// <summary>
        /// Adds a Sample
        /// </summary>
        /// <param name="sample">te sample to add</param>
        public void Add(Sample sample)
        {
#if ZUNE
            if (!(sample is ZuneSample))
                return;
#elif WINDOWS_PHONE
            if (!(sample is PhoneSample))
                return;
#endif

            if (!_Samples.Contains(sample))
                _Samples.Add(sample);
        }
        /// <summary>
        /// Removes a sample
        /// </summary>
        /// <param name="sample">the sample to remove</param>
        public void Remove(Sample sample)
        {
            if (_Samples.Contains(sample))
                _Samples.Remove(sample);
        }

        /// <summary>
        /// This Method should be called once, after all Samples have been added
        /// </summary>
        public void Activate()
        {
            if (_Samples.Count > 0)
                SetActiveSample(_Samples[0]);

            if (_Description == "")
                AutoGeneratedDescription();

            Vector2 str = Font.MeasureString("FPS: 60");
            _FpsPosition = new Vector2(
                Game.GraphicsDevice.PresentationParameters.BackBufferWidth - str.X - 10,
                Game.GraphicsDevice.PresentationParameters.BackBufferHeight - str.Y - 10);
        }

        void AutoGeneratedDescription()
        {
            string next = string.Empty;
            string prev = string.Empty;
#if !ZUNE
            next = "Keyboard: " + NextSampleKey.ToString() + " / Gamepad: " + NextSampleButton.ToString();
            prev = "Keyboard: " + PreviousSampleKey.ToString() + " / Gamepad: " + PreviousSampleButton.ToString();
            _Description = "Switch to the next sample:   " + next + "\n"
                + "Switch to the preveious sample:   " + prev + "\n\n"
                + "Press F1 or Back to hide the text.";
#else
            _Description = "Next Sample:    " + NextSampleButton.ToString() + "\n";
#endif

            _DescriptionPosition = new Vector2(10, Game.GraphicsDevice.PresentationParameters.BackBufferHeight - Font.MeasureString(_Description).Y - 10);
        }

        /// <summary>
        /// You may use this Method to show your own text to introduce the user how to switch between the samples.
        /// </summary>
        /// <param name="text">the text you want to display</param>
        public void SetIntroduction(string text)
        {
            _Description = text;
            
            _DescriptionPosition = new Vector2(10, Game.GraphicsDevice.PresentationParameters.BackBufferHeight - Font.MeasureString(_Description).Y - 10);
        }

        /// <summary>
        /// Use this Method to switch to a specified sample without pushing its key
        /// </summary>
        /// <param name="sample">the sample you want to switch to</param>
        public void SetActiveSample(Sample sample)
        {
            if(_ActiveSample != null)
                _ActiveSample.CallDeActivate();

            sample.Activate();
        }

        /// <summary>
        /// This method updates the current Sample and checks the Keys of every Sample.
        /// Call this method in the Update method of your game.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            float seconds = (float)gameTime.ElapsedGameTime.TotalSeconds;

            GamePadState gs = GamePad.GetState(PlayerIndex.One);
            KeyboardState ks = Keyboard.GetState();
            MouseState ms = Mouse.GetState();

            if (gs.IsButtonUp(NextSampleButton) && _PreviousButtons.IsButtonDown(NextSampleButton))
            {
                NextSample();
            }
#if !ZUNE
            else if (gs.IsButtonUp(PreviousSampleButton) && _PreviousButtons.IsButtonDown(PreviousSampleButton))
            {
                PreviousSample();
            }

            if (gs.IsButtonUp(_HelpButton) && _PreviousButtons.IsButtonDown(_HelpButton))
            {
                _IsDescriptionVisible = !_IsDescriptionVisible;
            }


            // show help
            if (ks.IsKeyUp(_HelpKey) && _PreviousKeys.IsKeyDown(_HelpKey))
            {
                _IsDescriptionVisible = !_IsDescriptionVisible;
            }

            // next / previos sample
            if (ks.IsKeyUp(NextSampleKey) && _PreviousKeys.IsKeyDown(NextSampleKey))
            {
                NextSample();
            }
            else if (ks.IsKeyUp(PreviousSampleKey) && _PreviousKeys.IsKeyDown(PreviousSampleKey))
            {
                PreviousSample();
            }
#endif

            // update active sample
            if (_ActiveSample != null)
            {
                _ActiveSample.Update(seconds, gs, _PreviousButtons, ks, _PreviousKeys, ms, _PreviousMouse);
            } 

            // set previous states to current
            _PreviousKeys = ks;
            _PreviousButtons = gs;
            _PreviousMouse = ms;
        }
        void NextSample()
        {
            for (int i = 0; i < _Samples.Count; i++)
            {
                if (_Samples[i] == _ActiveSample)
                {
                    SetActiveSample(_Samples[(i + 1) % (_Samples.Count)]);
                    break;
                }
            }
        }
        void PreviousSample()
        {
            for (int i = 0; i < _Samples.Count; i++)
            {
                if (_Samples[i] == _ActiveSample)
                {
                    SetActiveSample(_Samples[(i + _Samples.Count - 1) % _Samples.Count]);
                    break;
                }
            }
        }

        /// <summary>
        /// Sets the resolution immediately to the given values
        /// </summary>
        /// <param name="width">the width in pixels of the resolution</param>
        /// <param name="height">the height in pixels of the resolution</param>
        public void SetResolution(int width, int height)
        {
            if (_GraphicsDeviceManager.PreferredBackBufferWidth == width
                && _GraphicsDeviceManager.PreferredBackBufferHeight == height)
                return;

            _GraphicsDeviceManager.PreferredBackBufferWidth = width;
            _GraphicsDeviceManager.PreferredBackBufferHeight = height;
            _GraphicsDeviceManager.ApplyChanges();

            SetIntroduction(_Description);
        }

        /// <summary>
        /// Enables or disables full screen mode
        /// </summary>
        /// <param name="isFullScreen">if true, the whole screen is used to show the program. otherwise it is displayed in a window.</param>
        public void SetFullScreen(bool isFullScreen)
        {
            if (_GraphicsDeviceManager.IsFullScreen == isFullScreen)
                return;

            _GraphicsDeviceManager.IsFullScreen = isFullScreen;
            _GraphicsDeviceManager.ApplyChanges();
        }

        /// <summary>
        /// Calls the Draw() method of the active Sample and draws all the text onto the screen.
        /// Call this method in the Draw method of you Game.
        /// </summary>
        /// <param name="t">the GameTime object of the Draw method</param>
        /// <param name="batch">the SpriteBatch of your game</param>
        public void Draw(GameTime t, SpriteBatch batch)
        {
            if (_ActiveSample != null)
                _ActiveSample.Draw(batch);

            
            batch.Begin();
            
            if (_IsDescriptionVisible)
            {
                if (_ActiveSample != null)
                    batch.DrawString(Font, _ActiveSample.Descripton, new Vector2(5, 5), SampleTextColor);

                batch.DrawString(Font, _Description, _DescriptionPosition, ManagerTextColor);
            }
            else
            {
                batch.DrawString(Font, "Press F1", _DescriptionPosition, ManagerTextColor);
            }

            _FramesPerSecond = (int)Math.Round(1.0f / (float)t.ElapsedGameTime.TotalSeconds);
            batch.DrawString(Font, "FPS: " + _FramesPerSecond.ToString(), _FpsPosition, ManagerTextColor);


            batch.End();
        }


    }
}
